package cn.sylinx.horm.pool;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.Optional;

import javax.sql.DataSource;

import cn.sylinx.horm.dialect.EnumDbType;
import cn.sylinx.horm.util.GLog;

public abstract class AbstractDataSourceCreator<T extends DataSource> {

    @SuppressWarnings("unchecked")
    public T create(Map<String, Object> dsMap) {

        // dbtype 如果为空，则默认mysql
        String dbType = Optional.ofNullable(dsMap.get("dbtype")).orElseGet(EnumDbType.MYSQL::getValue)
                .toString();
        String driverClassName = Optional.ofNullable(dsMap.get("driver")).orElseGet(() -> "").toString();
        String url = Optional.ofNullable(dsMap.get("url")).orElseGet(() -> "").toString();
        String username = Optional.ofNullable(dsMap.get("username")).orElseGet(() -> "").toString();
        String password = Optional.ofNullable(dsMap.get("password")).orElseGet(() -> "").toString();

        T ds = createBaseDataSource(driverClassName, url, username, password, dbType);
        Map<String, Object> extConfig = (Map<String, Object>) dsMap.get("config");
        if (extConfig != null && !extConfig.isEmpty()) {
            setExtendConfig(ds, extConfig);
        }

        return ds;
    }

    protected void setExtendConfig(T cds, Map<String, Object> extConfig) {

        for (Map.Entry<String, Object> entry : extConfig.entrySet()) {
            String attr = entry.getKey();
            String value = entry.getValue().toString();
            setExtendConfigValue(cds, attr, value);
        }
    }

    private Method getClassSetMethodInner(Class<?> t, String attr, Class<?> parameterType) {

        String methodName = "set" + attr.substring(0, 1).toUpperCase() + attr.substring(1);
        try {
            return t.getDeclaredMethod(methodName, parameterType);

        } catch (Exception e) {
            // do nothin, ignore
            // GLog.error("在类：{}中没有找到方法：{}, 参数类型：{}", t.getName(), methodName,
            // parameterType.getName());
        }
        return null;
    }

    private Method getClassSetMethod(Class<?> t, String attr) {

        Class<?>[] parameterTypes = new Class<?>[]{int.class, long.class, boolean.class, String.class, Integer.class,
                Long.class, Boolean.class};

        for (Class<?> parameterType : parameterTypes) {
            Method m = getClassSetMethodInner(t, attr, parameterType);
            if (m != null) {
                return m;
            }
        }

        return null;
    }

    private Method getDataSourceSetMethod(Class<?> t, String attr) {

        Class<?> tmpClass = t;
        Method m = getClassSetMethod(tmpClass, attr);
        while (m == null) {
            tmpClass = tmpClass.getSuperclass();
            if ("java.lang.object".equalsIgnoreCase(tmpClass.getName())) {
                break;
            }
            m = getClassSetMethod(tmpClass, attr);
        }
        if (m != null) {
            GLog.debug("在类:{}中找到属性:{}", tmpClass.getName(), attr);
        }
        return m;
    }

    protected void setExtendConfigValue(Object t, String attr, String value) {

        try {
            Method m = getDataSourceSetMethod(t.getClass(), attr);
            if (m == null) {
                GLog.error("没有找到配置项:{}", attr);
                return;
            }
            m.setAccessible(true);
            Class<?> type = m.getParameterTypes()[0];
            Object targetValue = value;

            if (type == Boolean.class || type == boolean.class) {
                // 布尔
                targetValue = Boolean.valueOf(value);
            }

            if (type == Integer.class || type == int.class) {
                // int
                targetValue = Integer.valueOf(value);
            }

            if (type == Long.class || type == long.class) {
                // long
                targetValue = Long.valueOf(value);
            }

            if (type == Short.class || type == short.class) {
                // Short
                targetValue = Short.valueOf(value);
            }

            m.invoke(t, targetValue);

        } catch (Exception e) {
            GLog.error("setValue error", e);
        }
    }

    abstract T createBaseDataSource(String driver, String url, String username, String password, String dbType);

}
